package com.diagrammer
{
	import com.diagrammer.convertor.RenderConvertor;
	import com.diagrammer.convertor.VOConvertor;
	import com.diagrammer.event.BaseEvent;
	import com.diagrammer.event.LineEvent;
	import com.diagrammer.event.NodeEvent;
	import com.diagrammer.render.BaseRender;
	import com.diagrammer.render.LineRender;
	import com.diagrammer.render.NodeRender;
	import com.diagrammer.render.factory.IRenderFactory;
	import com.diagrammer.render.layouter.ILayouter;
	import com.diagrammer.vo.LineVO;
	import com.diagrammer.vo.NodeVO;
	import com.diagrammer.vo.factory.IVOFactory;
	
	import mx.containers.Canvas;

	/**
	 * 拓扑图类：将数据渲染成Render，新增、删除Render等，完成整个拓扑图的操作<br/>
	 * <li>各类（节点、线条）元素的焦点互不影响</li>
	 * <li>各类（节点、线条）元素放在不同的容器中，线条容器在节点容器的下面</li>
	 * @example 该类使用示例
	 * <listing version="1.0">
	 * //参数（非必须），如果未传入，则使用默认：NodeRender,LineRender,NodeVO,LineVO
	 * var diagrammer:Diagrammer = new Diagrammer(new VOFactory(), new RenderFactory());
	 * diagrammer.nodeFocusable = true;//设置节点是否可以获取焦点
	 * diagrammer.lineFocusable = true;//设置线条是否可以获取焦点
	 * diagrammer.multiFocusable = true;//设置是否可以同时多个节点或者线条获得焦点
	 * //...Layouter implements ILayouter，布局对象（非必须）
	 * diagrammer.layouter = new ...Layouter();
	 * //设置节点数据中标识属性名，可选择配置：nodeIDField,lineIDField,lineFromIDField,lineToIDField
	 * diagrammer.voConvertor.nodeIDField = "...";
	 * //获取节点和线条的容器
	 * var group:Designer = diagrammer.contentGroup;
	 * //设置容器大小
	 * group.percentWidth = 100;
	 * group.percentHeight = 100;
	 * //将容器添加到显示列表上
	 * addElement(group);
	 * //设置拓扑图的数据
	 * diagrammer.dataProvider = 
	 * 	&lt;data&gt;&lt;nodes&gt;&lt;node id="1"/&gt;&lt;node id="2"/&gt;&lt;/nodes&gt;&lt;lines&gt;&lt;line id="1" fromId="1" toId="2"/&gt;&lt;/lines&gt;&lt;/data&gt;;
	 * </listing>
	 * 
	 * @author Dwei <b>2012-02-27</b>
	 * 
	 */
	public class Diagrammer
	{
//============================================================================
//parameter
//============================================================================
		private var _contentGroup:Designer;
		
		/**
		 * 所有节点Render容器
		 * @private
		 */		
		protected var nodesGroup:Canvas;
		
		/**
		 * 所有线条Render容器
		 * @private
		 */		
		protected var linesGroup:Canvas;
		
		protected var _voConvertor:VOConvertor;
		
		protected var _renderConvertor:RenderConvertor;
		
		private var _layouter:ILayouter;
		
		private var _nodeFocusable:Boolean;
		
		private var _nodesFocusable:Boolean;
		
		private var _lineFocusable:Boolean;
		
		private var _linesFocusable:Boolean;
		
//		private var _multiFocusable:Boolean;
//============================================================================
//constructor
//============================================================================
		/**
		 * 构造函数
		 * @param voFactory VO工厂，如果为空，使用默认值com.diagrammer.vo.factory.VOFactory
		 * @param renderFactory Render工厂，如果为空，使用默认值com.diagrammer.view.spark.factory.RenderFactory
		 * 
		 */		
		public function Diagrammer(voFactory:IVOFactory = null, renderFactory:IRenderFactory = null)
		{
			//初始化linesGroup容器对象
			linesGroup = new Canvas();
			linesGroup.mouseEnabled = false;
			linesGroup.clipContent = false;
//			linesGroup.percentWidth = 100;linesGroup.percentHeight = 100;
			//初始化nodesGroup容器对象
			nodesGroup = new Canvas();
			nodesGroup.mouseEnabled = false;
			nodesGroup.clipContent = false;
//			nodesGroup.percentWidth = 100;nodesGroup.percentHeight = 100;
			//初始化contentGroup容器对象
			_contentGroup = new Designer();
			//添加子容器到contentGroup
			contentGroup.addChild(linesGroup);
			contentGroup.addChild(nodesGroup);
			//初始化转换器对象
			_voConvertor = new VOConvertor();
			voConvertor.factory = voFactory;
			_renderConvertor = new RenderConvertor();
			renderConvertor.factory = renderFactory;
			//初始化焦点相关
			_nodeFocusable = true;
			_nodesFocusable = true;
			_lineFocusable = true;
			_linesFocusable = true;
//			_multiFocusable = true;
			//初始化其它
			initialize();
		}
//============================================================================
//initialize
//============================================================================
		/**
		 * 初始化，由构造函数直接调用。子类可覆盖<br/>
		 * 执行该方法时，contentGroup、nodesGroup、linesGroup、voConvertor、renderConvertor已完成初始化
		 * 
		 */		
		protected function initialize():void
		{
			contentGroup.addEventListener(BaseEvent.RELAYOUT, relayout_handler);
			contentGroup.addEventListener(NodeEvent.UPDATE_LINES, updateLines_handler);
			contentGroup.addEventListener(NodeEvent.NODE_CLICK, nodeClick_handler);
			contentGroup.addEventListener(NodeEvent.NODE_CLICK_CTRL, nodeClickCtrl_handler);
			contentGroup.addEventListener(LineEvent.LINE_CLICK, lineClick_handler);
			contentGroup.addEventListener(LineEvent.LINE_CLICK_CTRL, lineClickCtrl_handler);
		}
//============================================================================
//handler
//============================================================================
		protected function relayout_handler(event:BaseEvent):void
		{
			layoutRenders();
		}
		
		protected function updateLines_handler(event:NodeEvent):void
		{
			var node:NodeRender = event.data as NodeRender;
			if (!node) return;
			var key:String;
			var line:LineRender;
			for each (key in node.vo.lines)
			{
				line = getLineRender(key);
				if (!line) continue;
				line.invalidateRender();
			}
		}
		
		protected function nodeClick_handler(event:NodeEvent):void
		{
			if (!nodeFocusable) return;
			var node:NodeRender = event.data as NodeRender;
			if (node) focusNodes([node.vo.ID]);
		}
		
		protected function nodeClickCtrl_handler(event:NodeEvent):void
		{
			if (!nodeFocusable) return;
			var node:NodeRender = event.data as NodeRender;
			var key:String = node.vo.ID;
			var result:Array = getFocusNodes();
			var index:int = result.indexOf(key);
			if (index == -1)
				result.unshift(key);
			else
				result.splice(index, 1);
			focusNodes(result);
		}
		
		protected function lineClick_handler(event:LineEvent):void
		{
			if (!lineFocusable) return;
			var line:LineRender = event.data as LineRender;
			if (line) focusLines([line.vo.ID]);
		}
		
		protected function lineClickCtrl_handler(event:LineEvent):void
		{
			if (!lineFocusable) return;
			var line:LineRender = event.data as LineRender;
			var key:String = line.vo.ID;
			var result:Array = getFocusLines();
			
			var index:int = result.indexOf(key);
			if (index == -1)
				result.unshift(key);
			else
				result.splice(index, 1);
			focusLines(result);
		}
//============================================================================
//public
//============================================================================
		/**
		 * 新增节点
		 * @param value 节点数据
		 * @return 返回新增的节点Render，如果新增失败，则返回null
		 * 
		 */		
		public function addNode(value:XML):NodeRender
		{
			//在记录中新增NodeVO
			var vo:NodeVO = voConvertor.addNode(value);
			if (!vo) return null;
			//在记录中新增NodeRender
			var render:NodeRender = renderConvertor.addNode(vo);
			//如果未成功将NodeVO转换成NodeRender，则将已新增的NodeVO删除
			if (!render)
			{
				voConvertor.removeNode(vo.ID);
				return null;
			}
			//初始化Render的diagrammer属性
			render.diagrammer = this;
			//在显示列表上添加NodeRender
			addElement(render);
			return render;
		}
		
		/**
		 * 删除指定标识的节点
		 * @param id
		 * 
		 */		
		public function removeNode(id:String):void
		{
			//获取需要删除的NodeRender
			var node:NodeRender = renderConvertor.nodesMap[id];
			//删除节点关联的线条
			var key:String;
			var keysBack:Array = node.vo.lines.concat();
			for each (key in keysBack)
			{
				removeLine(key);
			}
			//从显示列表删除NodeRender
			if (node) removeElement(node);
			//从记录中删除NodeRender
			renderConvertor.removeNode(id);
			//从记录中删除NodeVO
			voConvertor.removeNode(id);
		}
		
		/**
		 * 新增线条
		 * @param value 线条数据
		 * @return 返回新增的线条，如果新增失败，则返回null
		 * 
		 */		
		public function addLine(value:XML):LineRender
		{
			//在记录中新增LineVO
			var vo:LineVO = voConvertor.addLine(value);
			if (!vo) return null;
			//更新关联的节点关系（parents、children、lines）
			voConvertor.relateNodes(vo.ID);
			//在记录中新增LineRender
			var render:LineRender = renderConvertor.addLine(vo);
			//如果未成功将LineVO转换成LineRender，则将已新增的LineVO删除
			if (!render)
			{
				//先删除线条关联的节点之间的关系
				voConvertor.unRelateNodes(vo.ID);
				//删除线条VO的记录
				voConvertor.removeLine(vo.ID);
				return null;
			}
			//初始化Render的diagrammer属性
			render.diagrammer = this;
			//在显示列表上添加LineRender
			addElement(render);
			return render;
		}
		
		public function removeLine(id:String):void
		{
			//获取需要删除的LineRender
			var line:LineRender = renderConvertor.linesMap[id];
			//从显示列表删除LineRender
			if (line) removeElement(line);
			//从记录中删除LineRender
			renderConvertor.removeLine(id);
			//删除线条关联的节点之间的关系
			voConvertor.unRelateNodes(id);
			//从记录中删除LineVO
			voConvertor.removeLine(id);
		}
		
		/**
		 * 通过节点id，获取节点Render对象
		 * @param id 节点标识
		 * @return 如果未找到，返回null
		 * 
		 */		
		public function getNodeRender(id:String):NodeRender
		{
			return renderConvertor.nodesMap[id];
		}
		
		/**
		 * 通过节点id，获取节点VO对象
		 * @param id 节点标识
		 * @return 如果未找到，返回null
		 * 
		 */		
		public function getNodeVO(id:String):NodeVO
		{
			return voConvertor.nodesMap[id];
		}
		
		/**
		 * 通过线条id，获取线条Render对象
		 * @param id 线条标识
		 * @return 如果未找到，返回null
		 * 
		 */		
		public function getLineRender(id:String):LineRender
		{
			return renderConvertor.linesMap[id];
		}
		
		/**
		 * 通过线条id，获取线条VO对象
		 * @param id 线条标识
		 * @return 如果未找到，返回null
		 * 
		 */		
		public function getLineVO(id:String):LineVO
		{
			return voConvertor.linesMap[id];
		}
		
		/**
		 * 获取当前拥有焦点的节点标识集合
		 * @return 
		 * 
		 */		
		public function getFocusNodes():Array
		{
			var result:Array = [];
			var key:String;
			var node:NodeRender;
			for each (key in voConvertor.nodes)
			{
				node = renderConvertor.nodesMap[key];
				if (node && node.focus) result.push(key);
			}
			return result;
		}
		
		/**
		 * 获取当前拥有焦点的线条标识集合
		 * @return 
		 * 
		 */		
		public function getFocusLines():Array
		{
			var result:Array = [];
			var key:String;
			var line:LineRender;
			for each (key in voConvertor.lines)
			{
				line = renderConvertor.linesMap[key];
				if (line && line.focus) result.push(key);
			}
			return result;
		}
		
		/**
		 *	使传入节点获取焦点，其它节点失去焦点。
		 * 如果 <code>multiFocusable = false</code>，取传入集合中的第一个。
		 * 如果传入id集合为null或者集合中元素为0，所有节点都失去焦点
		 * @param values 需要获取焦点的节点id集合
		 * 
		 */		
		public function focusNodes(values:Array):void
		{
			if (!nodeFocusable) return;
			if (!values) values = [];
			var key:String;//当前节点id
			var index:int;//当前节点id在传入集合中的位置
			var node:NodeRender;//当前节点Render
			for each (key in voConvertor.nodes)
			{
				index = values.indexOf(key);
				node = renderConvertor.nodesMap[key];
				if (index == -1)//如果节点id不在传入集合中，失去焦点
					node.focus = false;
				else//如果节点id在传入集合中
				{
//					if (multiFocusable)//如果可以同时有多个焦点存在，获得焦点
					if (nodesFocusable)
						node.focus = true;
					else//如果只能有存在一个焦点对象
					{
						if (index == 0)//如果节点id是传入的第一个，获得焦点
							node.focus = true;
						else//如果节点id不是传入的第一个，失去焦点
							node.focus = false;
					}
				}
			}
		}
		
		/**
		 * 使传入线条获取焦点，其它线条失去焦点。
		 * 如果 <code>multiFocusable = false</code>，取传入集合中的第一个
		 * 如果传入id集合为null或者集合中元素为0，所有线条都失去焦点
		 * @param values 需要获取焦点的线条id集合
		 * 
		 */		
		public function focusLines(values:Array):void
		{
			if (!lineFocusable) return;
			if (!values) values = [];
			var key:String;//当前线条id
			var index:int;//当前线条id在传入集合中的位置
			var line:LineRender;//当前线条Render
			for each (key in voConvertor.lines)
			{
				index = values.indexOf(key);
				line = renderConvertor.linesMap[key];
				if (index == -1)//如果线条id不在传入集合中，失去焦点
					line.focus = false;
				else//如果线条id在传入集合中
				{
//					if (multiFocusable)//如果可以同时有多个焦点存在，获得焦点
					if (linesFocusable)
						line.focus = true;
					else//如果只能有存在一个焦点对象
					{
						if (index == 0)//如果线条id是传入的第一个，获得焦点
							line.focus = true;
						else//如果线条id不是传入的第一个，失去焦点
							line.focus = false;
					}
				}
			}
		}
//============================================================================
//protected
//============================================================================
		/**
		 * 布局Render
		 * 
		 */		
		protected function layoutRenders():void
		{
			if (layouter) layouter.layout(this);
		}
		
		/**
		 * 更新(移除不需要的、添加新增的)容器中的Render(Node、Line)
		 * 
		 */		
		protected function updateRenders():void
		{
			var renderChanged:Boolean = false;
			var index:int;
			var key:String;
			var oldRenders:Array = [];
			//----------------------------------------------------------------
			var node:NodeRender;
			//保存不需要的NodeRender
			for (index = 0;index < nodesGroup.numChildren;index++)
			{
				node = nodesGroup.getChildAt(index) as NodeRender;
				if (!node) continue;
				if (!(renderConvertor.nodesMap[node.vo.ID] === node)) oldRenders.push(node);
			}
			//添加新增的NodeRender
			for (key in renderConvertor.nodesMap)
			{
				node = renderConvertor.nodesMap[key] as NodeRender;
				if (!node || nodesGroup.owns(node)) continue;
				//初始化Render的diagrammer属性
				node.diagrammer = this;
				addElement(node);
				renderChanged = true;
			}
			//----------------------------------------------------------------
			var line:LineRender;
			//保存不需要的LineRender
			for (index = 0;index < linesGroup.numChildren;index++)
			{
				line = linesGroup.getChildAt(index) as LineRender;
				if (!line) continue;
				if (!(renderConvertor.linesMap[line.vo.ID] === line)) oldRenders.push(line);
			}
			//添加新增的LineRender
			for (key in renderConvertor.linesMap)
			{
				line = renderConvertor.linesMap[key] as LineRender;
				if (!line || linesGroup.owns(line)) continue;
				//初始化Render的diagrammer属性
				line.diagrammer = this;
				addElement(line);
				renderChanged = true;
			}
			//----------------------------------------------------------------
			//移除不需要的Render
			for (index = 0;index < oldRenders.length;index++)
			{
				removeElement(oldRenders[index]);
			}
			if (!renderChanged && oldRenders.length == 0) contentGroup.invalidateLayout();
		}
		
		/**
		 * 将Render添加到显示列表中
		 * @param value
		 * 
		 */		
		protected function addElement(value:BaseRender):void
		{
			if (value is NodeRender)
			{
				nodesGroup.addChild(value);
				//如果新增节点Render，重新布局Render
				contentGroup.invalidateLayout();
			}
			else if (value is LineRender)
			{
				linesGroup.addChild(value);
			}
//			else
//				;
		}
		
		/**
		 * 从显示列表上移除Render
		 * @param value
		 * 
		 */		
		protected function removeElement(value:BaseRender):void
		{
			if (value is NodeRender)
			{
				nodesGroup.removeChild(value);
				trace ("移除NodeRender:" + NodeRender(value).vo.ID);
				//如果新增节点Render，重新布局Render
				contentGroup.invalidateLayout();
			}
			else if (value is LineRender)
			{
				linesGroup.removeChild(value);
				trace ("移除LineRender:" + LineRender(value).vo.ID);
			}
//			else
//				;
		}
//============================================================================
//getter/setter
//============================================================================
		/**
		 * 拓扑图数据<br/>
		 * &lt;data&gt;<br/>
		 * &lt;nodes&gt;<br/>
		 * &lt;node .../&gt;<br/>
		 * ...<br/>
		 * &lt;node .../&gt;<br/>
		 * &lt;/nodes&gt;<br/>
		 * &lt;lines&gt;<br/>
		 * &lt;line .../&gt;<br/>
		 * ...<br/>
		 * &lt;line .../&gt;<br/>
		 * &lt;/lines&gt;<br/>
		 * &lt;/data&gt;
		 */		
		public function get dataProvider():XML
		{
			return voConvertor.dataProvider;
		}
		
		public function set dataProvider(value:XML):void
		{
			voConvertor.dataProvider = value;
			renderConvertor.voConvertor = voConvertor;
			updateRenders();
		}
		
		/**
		 * 所有Render容器
		 */
		public function get contentGroup():Designer
		{
			return _contentGroup;
		}
		
		/**
		 * VO转换器
		 * @default com.diagrammer.vo.factory.VOFactory
		 */
		public function get voConvertor():VOConvertor
		{
			return _voConvertor;
		}
		
		/**
		 * Render转换器
		 * @default com.diagrammer.view.spark.factory.RenderFactory
		 */
		public function get renderConvertor():RenderConvertor
		{
			return _renderConvertor;
		}
		
		/**
		 * 拓扑图的布局对象
		 */
		public function get layouter():ILayouter
		{
			return _layouter;
		}
		
		/**
		 * @private
		 */
		public function set layouter(value:ILayouter):void
		{
			_layouter = value;
		}
		
		/**
		 * 节点是否可以获取焦点
		 */
		public function get nodeFocusable():Boolean
		{
			return _nodeFocusable;
		}

		/**
		 * @private
		 */
		public function set nodeFocusable(value:Boolean):void
		{
			_nodeFocusable = value;
		}

		/**
		 * 是否允许同时多个节点获取焦点，如果《code>nodeFocusable=false</code>，则值恒为flase
		 */
		public function get nodesFocusable():Boolean
		{
			return _nodesFocusable && _nodeFocusable;
		}

		/**
		 * @private
		 */
		public function set nodesFocusable(value:Boolean):void
		{
			_nodesFocusable = value;
		}

		/**
		 * 线条是否可以获取焦点
		 */
		public function get lineFocusable():Boolean
		{
			return _lineFocusable;
		}
		
		/**
		 * @private
		 */
		public function set lineFocusable(value:Boolean):void
		{
			_lineFocusable = value;
		}

		/**
		 * 是否允许同时多个线条获取焦点，如果<code>lineFocusable=false</code>，则该值恒为false
		 */
		public function get linesFocusable():Boolean
		{
			return _linesFocusable && _lineFocusable;
		}

		/**
		 * @private
		 */
		public function set linesFocusable(value:Boolean):void
		{
			_linesFocusable = value;
		}
		
		/**
		 * 在同类（节点、线条）元素中是否可以同时存在多个焦点
		 */
//		public function get multiFocusable():Boolean
//		{
//			return _multiFocusable;
//		}
		
		/**
		 * @private
		 */
//		public function set multiFocusable(value:Boolean):void
//		{
//			_multiFocusable = value;
//		}


	}
}